﻿using DotNetty.Buffers;
using DotNetty.Transport.Channels;
using DotNettyExtensions.entity;
using EasyIot.Common;
using System;
using System.Collections.Concurrent;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Threading;

namespace DotNettyExtensions
{
    public class ServerMgr
    {
        public static long SocketTimeOut = 1 * 60 * 3;// 3分钟超时
        public static ConcurrentDictionary<string, IByteBuffer> dicSn2Buffer = new ConcurrentDictionary<string, IByteBuffer>();// 用于接收数据缓存 
        public static ConcurrentDictionary<string, UidEntity> dicSn2Client = new ConcurrentDictionary<string, UidEntity>();// 管理sn->client 断开时清理,指向同一个client 
        public static ConcurrentDictionary<string, UidEntity> dicUid2Client = new ConcurrentDictionary<string, UidEntity>();// 管理uid->client 下发指令时查询,指向同一个client 
        public static ConcurrentDictionary<IChannelHandlerContext, ChannelEntity> dicCtx2Info = new ConcurrentDictionary<IChannelHandlerContext, ChannelEntity>(); 

        public static void StartTask()
        {
            ThreadPool.QueueUserWorkItem(TimeOutClear);// 手动清理超时数据
        }

        static void TimeOutClear(object state)
        {
            int sleepTime = 60 * 1000;

            while (true)
            {
                Thread.Sleep(sleepTime);

                try
                {
                    var temp = dicCtx2Info.ToDictionary(entry => entry.Key, entry => entry.Value);// 浅拷贝
                    foreach (var item in temp)
                    {
                        var ctx = item.Key;
                        var client = item.Value;

                        if (CommonUtility.GetNowTimestamp() - client.LastTime > SocketTimeOut)
                        {
                            RemoveClient(ctx, "手动清理");
                        } 
                    } 
                }
                catch (Exception ex)
                {
                    Logger.Error("[Error] ServerMgr_TimeOutClear,errmsg=" + ex.Message);
                }
            }
        } 

        public static void RemoveClient(IChannelHandlerContext ctx, string errmsg)
        { 
            var channel = GetClient(ctx);

            if (channel != null)
            {
                string strProtocolType = "udp";
                if (ProtocolType.Tcp == channel.ProtocolType)
                {
                    strProtocolType = "tcp";
                    ctx.CloseAsync();// 只有tcp关闭链接
                }
                dicCtx2Info.TryRemove(ctx, out _);

                string sn = GetSN(channel.Remote); Logger.Info($"{strProtocolType}客户端退出,client：{sn},errmsg：{errmsg}");

                if (dicSn2Buffer.ContainsKey(sn))
                {
                    IByteBuffer byteBuf = dicSn2Buffer[sn];
                    //ReferenceCountUtil.Release(byteBuf);
                    dicSn2Buffer.TryRemove(sn, out _);
                }

                UidEntity uidEntity = GetUid(sn);
                if (uidEntity != null)
                {
                    dicUid2Client.TryRemove(uidEntity.Uid, out _);
                }

                dicSn2Client.TryRemove(sn, out _);
            }
        }

        public static string GetSN(EndPoint remote)
        { 
            return remote.ToString();
        }

        public static ChannelEntity GetClient(IChannelHandlerContext ctx)
        {
            ChannelEntity uidEndity;
            dicCtx2Info.TryGetValue(ctx, out uidEndity);
            return uidEndity;
        }

        public static void AddClient(IChannelHandlerContext ctx, ProtocolType protocoltype, EndPoint remote)
        {
            ChannelEntity value;
            if (dicCtx2Info.ContainsKey(ctx))
            {
                dicCtx2Info.TryRemove(ctx, out _);
            }
            value = new ChannelEntity() { ProtocolType = protocoltype, Remote = remote, LastTime = CommonUtility.GetNowTimestamp() };
            dicCtx2Info.TryAdd(ctx, value);
        } 

        public static UidEntity GetUid(string sn)
        {
            UidEntity uidEndity;
            dicSn2Client.TryGetValue(sn, out uidEndity); 
            return uidEndity;
        } 

        public static void AddUid(string sn, string uid, IChannelHandlerContext ctx, EndPoint remote)
        {
            UidEntity entity;
            if (!dicUid2Client.ContainsKey(uid))
            {
                entity = new UidEntity();
                dicUid2Client.TryAdd(uid, entity);
            }
            entity = dicUid2Client[uid];
            entity.Uid = uid;
            entity.Channel = ctx;
            entity.Remote = remote;

            if (dicSn2Client.ContainsKey(sn)) dicSn2Client.TryRemove(sn, out _);
            dicSn2Client.TryAdd(sn, entity);
        }

        public static bool GetWaitRecvRemain(IByteBuffer buf, int len)
        {
            return buf.ReaderIndex + len > buf.WriterIndex;
        }

        public static UidEntity CheckDevice(string strUid, ref string errmsg)
        {
            if (!dicUid2Client.ContainsKey(strUid))
            {
                errmsg = "Device not online";
                return null;
            }

            UidEntity uidEntity = dicUid2Client[strUid];
            var client = GetClient(uidEntity.Channel);

            if (null == uidEntity || null == uidEntity.Channel || null == client)
            {
                errmsg = "Device not online";
                return null;
            }

            if (CommonUtility.GetNowTimestamp() - client.LastTime > SocketTimeOut)
            {
                errmsg = "Device disconnected";
                return null;
            }

            return uidEntity;
        } 
    }
}